/*
 * Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-11-18     cmcc       the first version
 */
#include "ns_log.h"

#include <os_stddef.h>
#include <os_assert.h>
#include <cJSON.h>

#include "ns_face_recog.h"

#include "../util/fileUtil.h"
#include "../util/httpUtil.h"

static int _ns_face_detection(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Face_Detection *result);
static int _ns_face_location(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Face_Location *result);
static int _ns_face_property(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Face_Property *result);
static int _ns_human_property(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Human_Property *result);

static cJSON *_face_recog(char *baseUrl, char *token, char *contentBuffer, long contentSize, char *filePath);
static int _check_json(cJSON *json);

/**
 * Face detection.
 *
 * @param token authorize token
 * @param contentBuffer content of face
 * @param contentSize length of content
 * @param result NS_Face_Detection
 * @return the result of face detection, OS_EOK if success, else -OS_ERROR
 */
int ns_face_detection(char *token, char *contentBuffer, long contentSize, NS_Face_Detection *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_face_detection(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Face detection by file.
 *
 * @param token authorize token
 * @param filePath file path of face
 * @param result NS_Face_Detection
 * @return the result of face detection, OS_EOK if success, else -OS_ERROR
 */
int ns_face_detection_file(char *token, char *filePath, NS_Face_Detection *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(result);

    return _ns_face_detection(token, OS_NULL, -1, filePath, result);
}

/**
 * Face location.
 *
 * @param token authorize token
 * @param contentBuffer content of face
 * @param contentSize length of content
 * @param result NS_Face_Location
 * @return the result of face location, OS_EOK if success, else -OS_ERROR
 */
int ns_face_location(char *token, char *contentBuffer, long contentSize, NS_Face_Location *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_face_location(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Face location by file
 *
 * @param token authorize token
 * @param filePath file path of face
 * @param result NS_Face_Location
 * @return the result of face location, OS_EOK if success, else -OS_ERROR
 */
int ns_face_location_file(char *token, char *filePath, NS_Face_Location *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(result);

    return _ns_face_location(token, OS_NULL, -1, filePath, result);
}

/**
 * Face property.
 *
 * @param token authorize token
 * @param contentBuffer content of face
 * @param contentSize length of content
 * @param result NS_Face_Property
 * @return the result of face property, OS_EOK if success, else -OS_ERROR
 */
int ns_face_property(char *token, char *contentBuffer, long contentSize, NS_Face_Property *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_face_property(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Face property by file
 *
 * @param token authorize token
 * @param filePath file path of face
 * @param result NS_Face_Property
 * @return the result of face property, OS_EOK if success, else -OS_ERROR
 */
int ns_face_property_file(char *token, char *filePath, NS_Face_Property *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(result);

    return _ns_face_property(token, OS_NULL, -1, filePath, result);
}

/**
 * Human property.
 *
 * @param token authorize token
 * @param contentBuffer content of human
 * @param contentSize length of content
 * @param result NS_Human_Property
 * @return the result of human property, OS_EOK if success, else -OS_ERROR
 */
int ns_human_property(char *token, char *contentBuffer, long contentSize, NS_Human_Property *result)
{
    OS_ASSERT(token);
    OS_ASSERT(contentBuffer);
    OS_ASSERT(contentSize > 0);
    OS_ASSERT(result);

    return _ns_human_property(token, contentBuffer, contentSize, OS_NULL, result);
}

/**
 * Human property by file
 *
 * @param token authorize token
 * @param filePath file path of human
 * @param result NS_Human_Property
 * @return the result of face property, OS_EOK if success, else -OS_ERROR
 */
int ns_human_property_file(char *token, char *filePath, NS_Human_Property *result)
{
    OS_ASSERT(token);
    OS_ASSERT(filePath);
    OS_ASSERT(result);

    return _ns_human_property(token, OS_NULL, -1, filePath, result);
}

static int _ns_face_detection(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Face_Detection *result)
{
    cJSON *json = _face_recog(URL_FACE_DETECT_RECOG, token, contentBuffer, contentSize, filePath);
    if (_check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    result->faceNumber = atoi(cJSON_GetObjectItem(data, "face_num")->valuestring);

    cJSON *face_rect_json = cJSON_GetObjectItemCaseSensitive(data, "face_rect");
    if (face_rect_json == OS_NULL)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    char **face_rect = os_malloc(sizeof(char *) * result->faceNumber);
    cJSON *fr_element = OS_NULL;
    int index = 0;
    cJSON_ArrayForEach(fr_element, face_rect_json)
    {
        *(face_rect + index) = strdup(fr_element->valuestring);
        index++;
    }

    result->faceRect = face_rect;

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_face_location(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Face_Location *result)
{
    cJSON *json = _face_recog(URL_FACE_LOCATION_RECOG, token, contentBuffer, contentSize, filePath);
    if (_check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    result->faceNumber = cJSON_GetObjectItem(data, "face_num")->valueint;

    cJSON *points = cJSON_GetObjectItem(data, "points");

    char ***location_array = os_malloc(sizeof(char **) * result->faceNumber);
    int *point_count = os_malloc(sizeof(int) * result->faceNumber);

    int arry_index = 0;
    cJSON *point_array_json = OS_NULL;

    cJSON_ArrayForEach(point_array_json, points)
    {
        //save the size of the point_array_json
        int point_array_size = cJSON_GetArraySize(point_array_json);
        *(point_count + arry_index) = point_array_size;

        char **point_array = os_malloc(sizeof(char *) * point_array_size);
        int point_index = 0;
        cJSON *point_json = OS_NULL;
        cJSON_ArrayForEach(point_json, point_array_json)
        {
            //save every point in the point_array
            *(point_array + point_index) = strdup(cJSON_GetStringValue(point_json));
            point_index++;
        }

        //save the point_array to location_array
        *(location_array + arry_index) = point_array;
        arry_index++;
    }

    result->faceLocation = location_array;
    result->facePointCount = point_count;

    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_face_property(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Face_Property *result)
{
    cJSON *json = _face_recog(URL_FACE_PROPERTY_RECOG, token, contentBuffer, contentSize, filePath);
    if (_check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    result->faceNumber = cJSON_GetObjectItem(data, "face_num")->valueint;

    cJSON *properties_json = cJSON_GetObjectItem(data, "attribute");
    if (properties_json == OS_NULL)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    NS_Face_Property_Item *face_prop = os_malloc(sizeof(NS_Face_Property_Item) * (result->faceNumber));
    cJSON *property_element_json = OS_NULL;
    int index = 0;
    cJSON_ArrayForEach(property_element_json, properties_json)
    {
        (face_prop + index)->age = cJSON_GetObjectItem(property_element_json, "age")->valueint;
        (face_prop + index)->expression = strdup(cJSON_GetObjectItem(property_element_json, "expression")->valuestring);
        (face_prop + index)->gender = strdup(cJSON_GetObjectItem(property_element_json, "gender")->valuestring);
        (face_prop + index)->race = strdup(cJSON_GetObjectItem(property_element_json, "race")->valuestring);
        index++;
    }

    result->items = face_prop;
    cJSON_Delete(json);
    return OS_EOK;
}

static int _ns_human_property(char *token, char *contentBuffer, long contentSize, char *filePath, NS_Human_Property *result)
{
    cJSON *json = _face_recog(URL_HUMAN_PROPERTY_RECOG, token, contentBuffer, contentSize, filePath);
    if (_check_json(json) != OS_EOK)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    result->personNumber = cJSON_GetObjectItem(data, "person_num")->valueint;

    cJSON *properties_json = cJSON_GetObjectItem(data, "attribute");
    if (properties_json == OS_NULL)
    {
        cJSON_Delete(json);
        return -OS_ERROR;
    }

    NS_Human_Property_Item *human_prop = os_malloc(sizeof(NS_Human_Property_Item) * (result->personNumber));
    cJSON *property_element_json = OS_NULL;
    int index = 0;
    cJSON_ArrayForEach(property_element_json, properties_json)
    {
        (human_prop + index)->age = strdup(cJSON_GetObjectItem(property_element_json, "age")->valuestring);
        (human_prop + index)->backpack = strdup(cJSON_GetObjectItem(property_element_json, "backpack")->valuestring);
        (human_prop + index)->bag = strdup(cJSON_GetObjectItem(property_element_json, "bag")->valuestring);
        (human_prop + index)->boots = strdup(cJSON_GetObjectItem(property_element_json, "boots")->valuestring);

        (human_prop + index)->downColor = strdup(cJSON_GetObjectItem(property_element_json, "downColor")->valuestring);
        (human_prop + index)->downShort = strdup(cJSON_GetObjectItem(property_element_json, "downShort")->valuestring);
        (human_prop + index)->downType = strdup(cJSON_GetObjectItem(property_element_json, "downType")->valuestring);
        (human_prop + index)->facing = strdup(cJSON_GetObjectItem(property_element_json, "facing")->valuestring);

        (human_prop + index)->glasses = strdup(cJSON_GetObjectItem(property_element_json, "glasses")->valuestring);
        (human_prop + index)->handbag = strdup(cJSON_GetObjectItem(property_element_json, "handbag")->valuestring);
        (human_prop + index)->hat = strdup(cJSON_GetObjectItem(property_element_json, "hat")->valuestring);
        (human_prop + index)->holdObjectFront = strdup(cJSON_GetObjectItem(property_element_json, "holdObjectFront")->valuestring);

        (human_prop + index)->isFemale = strdup(cJSON_GetObjectItem(property_element_json, "isFemale")->valuestring);
        (human_prop + index)->longHair = strdup(cJSON_GetObjectItem(property_element_json, "longHair")->valuestring);
        (human_prop + index)->lowClothPants = strdup(cJSON_GetObjectItem(property_element_json, "lowClothPants")->valuestring);
        (human_prop + index)->shoesLight = strdup(cJSON_GetObjectItem(property_element_json, "shoesLight")->valuestring);

        (human_prop + index)->topLong = strdup(cJSON_GetObjectItem(property_element_json, "topLong")->valuestring);
        (human_prop + index)->upColor = strdup(cJSON_GetObjectItem(property_element_json, "upColor")->valuestring);
        (human_prop + index)->upShort = strdup(cJSON_GetObjectItem(property_element_json, "upShort")->valuestring);
        (human_prop + index)->upType = strdup(cJSON_GetObjectItem(property_element_json, "upType")->valuestring);

        index++;
    }

    result->items = human_prop;
    cJSON_Delete(json);
    return OS_EOK;
}

static cJSON *_face_recog(char *baseUrl, char *token, char *contentBuffer, long contentSize, char *filePath)
{
    //build url
    char url[200] = {0};
    strcat(url, baseUrl);
    strcat(url, "access_token=");
    strcat(url, token);
    LOG_EXT_D("url:%s", url);

    //send http request and get JSON response
    if (filePath != OS_NULL)
    {
        return http_post_file_base64(url, "image", filePath);
    }

    return http_post_content_base64(url, "image", contentBuffer, contentSize);
}

static int _check_json(cJSON *json)
{
    if (json == OS_NULL)
    {
        return -OS_ERROR;
    }

    cJSON *code = cJSON_GetObjectItem(json, "code");
    if (code->valueint != 200)
    {
        LOG_EXT_E("invalid response, code:%d, msg:%s", code->valueint, (cJSON_GetObjectItem(json, "msg"))->valuestring);
        return -OS_ERROR;
    }

    cJSON *data = cJSON_GetObjectItem(json, "data");
    if (data == OS_NULL)
    {
        LOG_EXT_E("no data elements");
        return -OS_ERROR;
    }

    return OS_EOK;
}
